package com.rytong.tools.utils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Comparator;
import com.rytong.tools.crypto.HMac;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Environment;
import android.os.StatFs;

/**
 * 图片缓存类，主要是用来缓存网络图片，缓存策略:
 * （1）LRU算法，固定缓存图片数量(max_num)，当图片数量超出max_num时，将缓存中最近用的最少的图片删除。
 * （2）FTU算法，固定每张图片的缓存时限，以最后一次使用算起，超过时限后删除。 
 * （3）FMU算法，在存储器中固定一定大小的存储空间，超过固定空间后 将缓存中占用最大尺寸的图片删除。使用时只需要向方法体中传递图片的URL即可。
 * 
 * 项目中主要是采用的是：
 * 1.存在sdcard，暂不作数量限制和缓存控件大小，但是会删除过期图片
 * 2.不存在sdcard，存储与data目录中，限制图片数量，会删除过期图片
 * @author cheng.l 2013-07-30 下午04:36:38
 */
public class CacheBitmapUtils {
	private static final String TAG = "---CacheBitmap---";
	//限制图片数量的最大值
	private static final int MAX_LEGNTH = 200;
	//图片缓存目录
	private static final String CACHE_PATH = "/jtyh/cache/";
	//缓存的文件扩展名
	private static final String WHOLESALE_CONV = ".cash";
	//限制过期的时间,设置为三天
	private static final long PAST_TIME = 3;
	//需要缓存图片的空间,单位（MB）
	public static final long FREE_SPACE_NEED = 0;
	//缓存目录的空间,单位（MB）
	public static final long CACHE_SIZE = 0;

	//过期时间，单位（毫秒）
	private long pastTime = 0;
	//缓存的目录路径
	private String cachePath = null;
	//当前存储位置是sdcard还是data目录
	private boolean isSdcard = true;
	public CacheBitmapUtils() {
		cachePath = getCacheDirectory();
		pastTime = (long)(PAST_TIME * 24 * 60 * 60 * 1000);
		removeCache(cachePath);
	}

	/**
	 * 得到缓存的bitmap
	 * 
	 * @param url
	 */
	public Bitmap getCacheBitmap(String url) {
		if (null == url || "".equalsIgnoreCase(url)) {
			return null;
		}
		
		Bitmap bitmap = null;
		String fileName = convertUrlToFileName(url);
		File file = new File(cachePath + fileName);
		if (file.exists()) {
			bitmap = decodeFile(Uri.fromFile(file));
		}
		if(null != bitmap){
			//设置文件的最后修改时间
			updateFileTime(cachePath + fileName);
			Utils.LogD(TAG, "Get a bitmap from cache!");
		}else{
			//删除文件
			deleteFile(file);
		}
		return bitmap;
	}

	/**
	 * 判断图片是否已经再huan'cun'zho
	 * @param url
	 * @return
	 */
	public boolean isInCache(String url){
		if (null == url || "".equalsIgnoreCase(url)) {
			return false;
		}
		String fileName = convertUrlToFileName(url);
		File file = new File(cachePath + fileName);
		if (file.exists()) {
			return true;
		}
		return false;
	}
	/**
	 * 缓存图片到本地
	 * 
	 * @param bm
	 * @param url
	 */
	public void cacheBitmap(Bitmap bm, String url) {
		if (bm == null) {
			Utils.LogD(TAG," trying to save a null bitmap");
			return;
		}
		
		String fileName = convertUrlToFileName(url);
		File file = new File(cachePath + fileName);
		if(file.exists()){
			return;
		}
		try {
			file.createNewFile();
			OutputStream outStream = new FileOutputStream(file);
			file.setLastModified(System.currentTimeMillis());
			bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
			outStream.flush();
			outStream.close();
			Utils.LogD(TAG,"Bitmap is saved to in Cache");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 缓存图片到本地 
	 * @param url
	 * @param inputStream
	 */
	public void cacheFile(String url, InputStream inputStream) {
		OutputStream outputStream = null;
		try {
			String fileName = convertUrlToFileName(url);
			File file = new File(cachePath + fileName);
			file.setLastModified(System.currentTimeMillis());
			outputStream = new FileOutputStream(file);
			copyStream(inputStream, outputStream);
			Utils.LogD(TAG, "save a bitmap to cache");
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if(outputStream != null) {
				try {
					outputStream.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	
	/**
	 * 将inpustream copy 成 outpustream
	 * @param is
	 * @param os
	 */
	private void copyStream(InputStream is, OutputStream os) {
		final int buffer_size = 1024;
		try {
			byte[] bytes = new byte[buffer_size];
			for (;;) {
				int count = is.read(bytes, 0, buffer_size);
				if (count == -1)
					break;
				os.write(bytes, 0, count);
			}
		} catch (Exception ex) {
			Utils.printException(ex);
		}
	}
	
	/**
	 * 将url转换为文件名,对fileName作sha1值加密处理
	 * @param url
	 * @return
	 */
	public String convertUrlToFileName(String url){
		if (null == url || "".equalsIgnoreCase(url)) {
			return null;
		}
		String fileName = null;

		int index = url.lastIndexOf(".");
		if (index != -1) {
			url = url.substring(0, index);
		}
		
		fileName = shaFileName(url)+WHOLESALE_CONV;
		return fileName;
	}

	/**
	 * 读取文件二进制
	 * 
	 * @param file
	 * @return
	 */
	public byte[] readBitmap(File file) {
		byte[] buffer = null;
		FileInputStream fis = null;
		try {
			fis = new FileInputStream(file);
			int len = fis.available();
			buffer = new byte[len];
			fis.read(buffer);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
				}
			}
		}
		return buffer;
	}

	/**
	 * 对文件名作sha1值处理
	 * 
	 * @param fileName
	 */
	public String shaFileName(String fileName) {
		String shaValue = null;
		if (null == fileName) {
			return null;
		}
		try {
			byte[] temp = HMac.SHA1(fileName.getBytes("UTF-8"));
			if (temp != null) {
				shaValue = HMac.byteArrayToHexString(temp);
			}
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return shaValue;
	}

	/**
	 * 得到缓存的目录
	 * 
	 * @return
	 */
	private String getCacheDirectory() {
		String path = null;
		if (existSDCard()) {
			isSdcard = true;
			path = android.os.Environment.getExternalStorageDirectory() + CACHE_PATH;
		} else {
			isSdcard = false;
			path = Utils.getActivity().getFilesDir().getPath() + CACHE_PATH;
		}
		File file = new File(path);
		if (!file.exists()) {
			file.mkdirs();
		}
		return path;
	}

	/**
	 * 删除单个文件
	 * @param file
	 */
	public void deleteFile(File file){
		if(file.exists()){
			file.delete();
		}
	}
	
	/**
	 * 递归调用删除目录和目录下的文件
	 * 
	 * @param file
	 */
	public void deleteDirectory(File file) {
		if (file.isDirectory()) {
			File[] files = file.listFiles();
			if (files.length > 0) {
				for (int i = 0; i < files.length; i++) {
					deleteDirectory(files[i]);
				}
			}
			file.delete();
		} else {
			file.delete();
		}
	}

	/**
	 * 修改文件的最后修改时间
	 * 
	 * @param dir
	 * @param fileName
	 */
	public void updateFileTime(String path) {
		File file = new File(path);
		long newModifiedTime = System.currentTimeMillis();
		file.setLastModified(newModifiedTime);
	}

	/**
	 * 删除图片缓存，主要是是删除过期的图片
	 * @param path
	 */
	private void removeCache(String dirPath){
		if(dirPath == null || "".equalsIgnoreCase(dirPath)){
			return;
		}
		
		File dir = new File(dirPath);
		File[] files = dir.listFiles();
		if(null == files || files.length == 0){
			return;
		}
		
		//删除过期的图片
		for(int i = 0;i < files.length;i++){
			removeExpiredCache(files[i]);
		}
		
		if(isSdcard){
			//如果data中也存在，删除data中所有缓存的图片
			File dataFile = new File(Utils.getActivity().getFilesDir().getPath() + "/jtyh/");
			if (dataFile.exists()) {
				deleteDirectory(dataFile);
			}
		}else{
			//删除超过数量的图片
			files = dir.listFiles();
			if(files.length > MAX_LEGNTH){
				int expires = files.length - MAX_LEGNTH;
				Arrays.sort(files, new FileLastModifSort());
				for(int i = 0;i < files.length;i++){
					if(files[i].getName().endsWith(WHOLESALE_CONV) && i <= expires-1){
						files[i].delete();
					}
				}
			}
		}
	}
	
	/**
	 * 删除过期文件
	 * 
	 * @param dirPath
	 * @param filename
	 */
	private void removeExpiredCache(File file) {
		long time = System.currentTimeMillis() - file.lastModified();
		if(file.getName().endsWith(WHOLESALE_CONV) && time >= pastTime) {
			if(file.exists()){
				Utils.LogD(TAG, "remove a expired bitmap in chache");
				file.delete();
			}
		}
	}

	/**
	 * TODO 根据文件的最后修改时间进行排序 *
	 */
	class FileLastModifSort implements Comparator<File> {
		public int compare(File arg0, File arg1) {
			if (arg0.lastModified() > arg1.lastModified()) {
				return 1;
			} else if (arg0.lastModified() == arg1.lastModified()) {
				return 0;
			} else {
				return -1;
			}
		}
	}


	/**
	 * 判断SDCard是否存在
	 * 
	 * @return
	 */
	private boolean existSDCard() {
		if (android.os.Environment.getExternalStorageState().equals(
				android.os.Environment.MEDIA_MOUNTED)) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 得到sdcard的可用空间
	 * 
	 * @return
	 */
	public long getSDFreeSize() {
		// 取得SD卡文件路径
		File path = Environment.getExternalStorageDirectory();
		StatFs sf = new StatFs(path.getPath());
		// 获取单个数据块的大小(Byte)
		long blockSize = sf.getBlockSize();
		// 空闲的数据块的数量
		long freeBlocks = sf.getAvailableBlocks();

		// 返回SD卡空闲大小
		// return freeBlocks * blockSize; //单位Byte
		// return (freeBlocks * blockSize)/1024; //单位KB
		return (freeBlocks * blockSize) / 1024 / 1024; // 单位MB
	}

	/**
	 * 得到SDCard的总空间
	 * 
	 * @return
	 */
	public long getSDAllSize() {
		// 取得SD卡文件路径
		File path = Environment.getExternalStorageDirectory();
		StatFs sf = new StatFs(path.getPath());
		// 获取单个数据块的大小(Byte)
		long blockSize = sf.getBlockSize();
		// 获取所有数据块数
		long allBlocks = sf.getBlockCount();

		// 返回SD卡大小
		// return allBlocks * blockSize; //单位Byte
		// return (allBlocks * blockSize)/1024; //单位KB
		return (allBlocks * blockSize) / 1024 / 1024; // 单位MB
	}

	// 通过Uri解析图片，解决图片过大引起OOM的问题
	public static Bitmap decodeFile(Uri uri) {
		Bitmap bitmap = null;
		if (null != uri) {
			File file = new File(uri.getPath());
			if (file.exists()) {
				InputStream is;
				try {
					is = new FileInputStream(file);
					byte[] data = readStream(is);
					bitmap = getBitmap(data);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		return bitmap;
	}

	// 将byte转换成图片
	public static Bitmap getBitmap(byte[] data) {
		// TODO Auto-generated method stub
		BitmapFactory.Options opts = new BitmapFactory.Options();
		opts.inJustDecodeBounds = true;
		BitmapFactory.decodeByteArray(data, 0, data.length, opts);

		opts.inSampleSize = computeSampleSize(opts, -1, 512 * 512);
		opts.inJustDecodeBounds = false;
		try {
			Bitmap bmp = BitmapFactory.decodeByteArray(data, 0, data.length,opts);
			return bmp;
		} catch (OutOfMemoryError err) {
			err.printStackTrace();
		}
		return null;
	}

	// 读取字节流
	public static byte[] readStream(InputStream inStream) throws Exception {
		ByteArrayOutputStream outstream = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024];
		int len = -1;
		while ((len = inStream.read(buffer)) != -1) {
			outstream.write(buffer, 0, len);
		}
		outstream.close();
		inStream.close();
		return outstream.toByteArray();
	}

	private static int computeSampleSize(BitmapFactory.Options options,
			int minSideLength, int maxNumOfPixels) {
		int initialSize = computeInitialSampleSize(options, minSideLength,
				maxNumOfPixels);

		int roundedSize;
		if (initialSize <= 8) {
			roundedSize = 1;
			while (roundedSize < initialSize) {
				roundedSize <<= 1;
			}
		} else {
			roundedSize = (initialSize + 7) / 8 * 8;
		}

		return roundedSize;
	}

	// 计算一个缩放参数
	private static int computeInitialSampleSize(BitmapFactory.Options options,
			int minSideLength, int maxNumOfPixels) {
		double w = options.outWidth;
		double h = options.outHeight;

		int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math
				.sqrt(w * h / maxNumOfPixels));
		int upperBound = (minSideLength == -1) ? 128 : (int) Math.min(
				Math.floor(w / minSideLength), Math.floor(h / minSideLength));

		if (upperBound < lowerBound) {
			// return the larger one when there is no overlapping zone.
			return lowerBound;
		}

		if ((maxNumOfPixels == -1) && (minSideLength == -1)) {
			return 1;
		} else if (minSideLength == -1) {
			return lowerBound;
		} else {
			return upperBound;
		}
	}
}